﻿using DoNet.Common.Helpers;
using DoNet.Common.Result;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Collections.Specialized;
using System.Reflection;
using System.Security.Claims;
using System.Web;

namespace WechatMp.Web
{
    /// <summary>
    /// 功能权限授权验证
    /// </summary>
    public class AuthorizationFilter : AuthorizeAttribute, IAuthorizationFilter
    {
        /// <summary>
        /// 授权验证
        /// </summary>
        /// <param name="context"></param>
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
            //匿名访问，不需要token认证、签名和登录
            var allowanyone = controllerActionDescriptor?.MethodInfo.GetCustomAttribute(typeof(AllowAnonymousAttribute), true);
            if (allowanyone == null)
            {
                CommonResult result = new CommonResult();
                var request = context.HttpContext.Request;
                var requestHeaders = request.Headers;
                //需要token认证
                string authHeader = requestHeaders["Authorization"].ToString();//Header中的token
                if (!string.IsNullOrEmpty(authHeader))
                {
                    string token = string.Empty;
                    if (authHeader.Length > 7)
                    {
                        token = authHeader.Substring(7);
                    }
                    TokenProvider tokenProvider = new TokenProvider();
                    var validateToken = tokenProvider.ValidateToken(token);
                    // token验证失败
                    if (validateToken.Success)
                    {
                        #region 签名验证
                        var signHeader = requestHeaders["sign"].SingleOrDefault() ?? string.Empty;
                        bool boolSign = string.IsNullOrWhiteSpace(signHeader) ? false : true;
                        var isSign = controllerActionDescriptor?.MethodInfo.GetCustomAttribute(typeof(NoSignRequiredAttribute), true);
                        //需要签名验证
                        if (isSign == null && boolSign)
                        {
                            string appId = requestHeaders["appId"].SingleOrDefault() ?? string.Empty;
                            string appSecret = "";
                            string nonce = requestHeaders["nonce"].SingleOrDefault() ?? string.Empty;
                            long timeStamp;
                            if (!long.TryParse(requestHeaders["timeStamp"].SingleOrDefault(), out timeStamp))
                            {
                                timeStamp = default;
                            }
                            string signature = requestHeaders["signature"].SingleOrDefault() ?? string.Empty;

                            NameValueCollection form = HttpUtility.ParseQueryString(request.QueryString.ToString());
                            var data = string.Empty;
                            if (form.Count > 0)
                            {
                                data = SignHelper.GetQueryString(form);
                            }
                            else
                            {
                                using (Stream stream = request.Body)
                                {
                                    using (StreamReader streamReader = new StreamReader(stream))
                                    {
                                        data = streamReader.ReadToEndAsync().Result;
                                    }
                                }
                            }
                            var resultSign = SignHelper.CheckSign(appId, appSecret, nonce, timeStamp, signature, data);
                            if (!resultSign.Success)
                            {
                                context.Result = new JsonResult(resultSign);
                                return;
                            }
                        }
                        #endregion

                        #region 是否需要验证用户登录以及相关的功能权限
                        //是否需要用户登录
                        var isDefined = controllerActionDescriptor?.MethodInfo.GetCustomAttribute(typeof(NoPermissionRequiredAttribute));
                        //不需要登录
                        if (isDefined == null)
                        {
                            //需要登录和验证功能权限
                            var claimlist = result.ResData as List<Claim>;
                            if (claimlist != null && claimlist.Count > 0)
                            {
                                string userId = EncodeHelper.AES_Decrypt(claimlist[3].Value);
                                string account = EncodeHelper.AES_Decrypt(claimlist[2].Value);
                                string role = EncodeHelper.AES_Decrypt(claimlist[4].Value);

                                CurrentUser user = new CurrentUser
                                {
                                    UserId = Convert.ToInt64(claimlist[3].Value),
                                    Account = claimlist[2].Value,
                                    Role = claimlist[4].Value
                                };

                                var claims = new[] {
                                   new Claim(ClaimConst.UserId, userId),
                                   new Claim(ClaimConst.UserName, account),
                                   new Claim(ClaimConst.Role, role)
                                };
                                var identity = new ClaimsIdentity(claims);
                                var principal = new ClaimsPrincipal(identity);
                                context.HttpContext.User = principal;
                                bool isAdmin = Permission.IsAdmin(user);
                                if (!isAdmin)
                                {
                                    var authorizeAttribute = controllerActionDescriptor?.MethodInfo.GetCustomAttributes(typeof(CustomerAuthorizeAttribute), true).OfType<CustomerAuthorizeAttribute>().FirstOrDefault();
                                    if (authorizeAttribute != null)
                                    {
                                        string function = authorizeAttribute.Function;
                                        if (!string.IsNullOrEmpty(function))
                                        {
                                            string functionCode = controllerActionDescriptor?.ControllerName + "/" + function;
                                            bool bl = Permission.HasFunction(functionCode, userId);
                                            if (!bl)
                                            {
                                                result.ErrCode = "40006";
                                                //result.ErrMsg = ErrCode.err40006;
                                                context.Result = new JsonResult(result);
                                            }
                                        }
                                    }
                                }
                                return;
                            }
                            else
                            {
                                result.ErrCode = "40008";
                                //result.ErrMsg = ErrCode.err40008;
                                context.Result = new JsonResult(result);
                            }
                        }
                        else
                        {
                            return;
                        }
                        #endregion
                    }
                    else
                    {
                        context.Result = new JsonResult(result);
                    }
                }
                else
                {
                    result.ErrCode = "40004";
                    //result.ErrMsg = ErrCode.err40004;
                    context.Result = new JsonResult(result);
                    return;
                }
            }
            else
            {
                return;
            }
        }
    }
}
